Skip to content

fix(cli): check port availability before starting SSH forward#309

Merged
drew merged 8 commits intomainfrom
check-port-before-forward/an
Mar 15, 2026
Merged

fix(cli): check port availability before starting SSH forward#309
drew merged 8 commits intomainfrom
check-port-before-forward/an

Conversation

@drew
Copy link
Copy Markdown
Collaborator

@drew drew commented Mar 14, 2026

Summary

  • Add a pre-flight port availability check before attempting SSH port forwards
  • If the port is occupied by an existing openshell forward, the error includes the openshell forward stop command
  • If occupied by an unknown process, the error suggests lsof -i :<port> -sTCP:LISTEN
  • Applied to both CLI (sandbox create --forward, forward start) and TUI paths

Problem

When running sandbox create --forward <port> and the port was already in use, the SSH process would fail with a cryptic ssh exited with status exit status: 255. The sandbox was still created (with keep=true) but the user was never connected — left with an orphaned sandbox and no actionable guidance.

Changes

  • crates/openshell-core/src/forward.rs — New check_port_available(port) function that attempts TcpListener::bind and returns actionable error messages
  • crates/openshell-cli/src/ssh.rs — Call check_port_available at the top of sandbox_forward(), before SSH session setup
  • crates/openshell-tui/src/lib.rs — Guard in start_port_forwards() loop that skips unavailable ports with a warning

Testing

  • Two new unit tests: check_port_available_free_port and check_port_available_occupied_port
  • mise run pre-commit passes
  • cargo test -p openshell-core -- forward — all 16 tests pass

Previously, sandbox create --forward and forward start would attempt
the SSH port forward without checking if the port was already in use,
resulting in a cryptic 'ssh exited with status 255' error. The sandbox
would still be created but the user would not be connected.

Add a pre-flight check that tries to bind the port before spawning SSH.
If the port is occupied by an existing openshell forward, the error
message includes the stop command. If occupied by another process, it
suggests lsof to identify it. Applied to both CLI and TUI paths.
@drew drew self-assigned this Mar 14, 2026
drew added 6 commits March 14, 2026 16:15
Extend --forward to accept [bind_address:]port syntax following SSH -L
conventions. For example, --forward 0.0.0.0:8080 binds on all interfaces
instead of just localhost.

Add ForwardSpec type in openshell-core that handles parsing, SSH -L arg
generation, and display formatting. Thread it through the CLI, TUI, and
all forwarding call sites. The port availability check also uses the
specified bind address.
Instead of falling back to the last-used sandbox when no name is
provided, scan the forwards PID directory for the matching port.
Ports are unique across sandboxes so the port alone is sufficient
to identify which forward to stop.
check_port_available only tested the requested bind address (e.g.
127.0.0.1), so a server listening on [::] (IPv6 wildcard) would not
be detected. Now also runs lsof to catch any listener on the port
regardless of address family. The error message includes the lsof
output and a kill hint so users can free the port.
Move the port availability check to the top of sandbox_create so it
runs before the sandbox is provisioned. Previously the check only
happened inside sandbox_forward, after the sandbox was already
created — leaving an orphaned sandbox if the port was occupied.
Persist the bind address in the forward PID file as a third
tab-separated field and display it as a BIND column in
'openshell forward list'. Old PID files without the field
default to 127.0.0.1.
…d path

The check_port_available call in start_port_forwards ran synchronous I/O
(TcpListener::bind and lsof subprocess) inside a tokio::spawn async task,
blocking the tokio worker thread and stalling the TUI event loop. This
caused the TUI to hang after dropping to the shell for exec commands.

The CLI paths (sandbox_create and sandbox_forward) already perform this
check in appropriate synchronous contexts, and SSH itself will fail
gracefully if the port is unavailable.
@drew drew merged commit dc817c3 into main Mar 15, 2026
9 checks passed
@drew drew deleted the check-port-before-forward/an branch March 15, 2026 04:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants